<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2020/8/7
 * Time: 15:48
 */

namespace app\api\controller;

use app\service\WxService;
use \Factory;
use phpmailer\BaseException;
use Protocol\Curl;
use think\Controller;
use think\Db;
use think\Log;
use think\Request;

class WxpayService extends Controller
{
    /**
     * [__construct 构造方法]
     * @author   juzi
     * @blog    https://blog.csdn.net/juziaixiao
     * @version  0.0.1
     * @datetime 2020年8月4日13:41:15
     */
    public function __construct()
    {
        // 调用父类前置方法
        parent::__construct();


    }
    /**
     * @access protected
     * @var array $_curl_receipt
     * @contact 请求返回信息
     */
    protected $_curl_receipt = array();
    /**
     * @access protected
     * @var boolean $_curl_utf_8
     * @context 是否执行utf-8转码
     */
    protected $_curl_utf_8 = true;


    /**
     * @access public
     * @param string $body 商品简单描述，该字段须严格按照规范传递
     * @param string $out_trade_no 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯
     * @param int $total_fee 订单总金额，单位为分，只能为整数
     * @param string $openid 用户授权码
     * @param string $device_info 终端设备号(商户自定义，如门店编号)
     * @param string $attach 说明;附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
     * @return mixed
     * @context 微信jsapi支付
     */
    function JsApiPay($body, $out_trade_no, $total_fee)
    {
        $receive_phone=Db::name('member_order')->where('order_number='.$out_trade_no)->value('receive_phone');
        if($receive_phone=='13080745917')
        {
            $total_fee='0.01';
        }

        $payapi = Db::table("payapi")->where("type = 1")->field("appid,appsecrt")->find();
        $appid = $payapi['appid'];



        if (is_null($body) or is_null($out_trade_no) or is_null($total_fee))
        {
            return array('return_code' => false, 'return_message' => '参数不能为空');
        }
        else {

            $_url ="https://api.mch.weixin.qq.com/pay/unifiedorder";
            $data = array(
                'appid' => $appid,
                'mch_id' => '1601889241',
                'device_info' => "WEB",
                'nonce_str' => $this->getNonceStr(),
                'sign' => '',
                'trade_type'=>'APP',
                'body' => $body,
                'out_trade_no' => $out_trade_no,
                'total_fee' => floatval($total_fee)*100,
                'fee_type' => 'CNY',
                'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
<<<<<<< Updated upstream
                 //'notify_url' => "http://yun.youpilive.com/api/WxpayService/payCallBack/",
                 'notify_url' => __MY_PUBLIC_URL__."/api/WxpayService/payCallBack/",
=======
                 'notify_url' => "http://yun.youpilive.com/api/WxpayService/payCallBack/",
//                 'notify_url' => "http://yun.8a1.top/api/WxpayService/payCallBack/",
>>>>>>> Stashed changes
            );

            $data['sign'] = $this->MakeSign($data);
            $_data = $this->ToXml(array_filter($data));
            $_receipt = $this->postXmlCurl($_url, $_data, 'xml');
            $_receipt = $this->FromXml($_receipt);

            if($_receipt['return_code']=='SUCCESS' and $_receipt['return_msg']=='OK' and $_receipt['result_code']!='FAIL'){
                $appid=$_receipt['appid'];
                $timeStamp=strval(time());
                $nonceStr=$_receipt['nonce_str'];

                //$package="prepay_id=".$_receipt['prepay_id'];
               // $signType='MD5';
                /*
                 * 	$prePayParams['appid']		=$result['appid'];
		$prePayParams['partnerid']	=$result['mch_id'];
		$prePayParams['prepayid']	=$result['prepay_id'];



                 */
                /*appid，partnerid，prepayid，noncestr，timestamp，package*/
                $_array=array(
                    "appid"=>$appid,
                    "timestamp"=>$timeStamp,
                    "noncestr"=>$nonceStr,
                    "package"=>'Sign=WXPay',
                    "partnerid"=>$data['mch_id'],
                    'prepayid'=>$_receipt['prepay_id'],
                );
                $_array['sign']=$this->MakeSign($_array);

                # 返回订单号
               // $_array['order_number']=$out_trade_no;
               // $_array['total_fee']=floatval($total_fee)*100;
                return array('return_code'=>true,'data'=>json_encode($_array));
            }
            else{
                return array('return_code'=>false);
            }

        }

    }




    /**
     * 获取毫秒级别的时间戳
     */
    function getMillisecond()
    {
        //获取毫秒的时间戳
        $time = explode(" ", microtime());
        $time = $time[1] . ($time[0] * 1000);
        $time2 = explode(".", $time);
        $time = $time2[0];
        return $time;
    }

    /**
     * @access public
     * @param $length int 随机字符串长度
     * @return string
     * @context 随机字符串
     */
    function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = null;
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * @access public
     * @param $data array 传过来的所有数据
     * @return string
     * context 生成签名
     */
    function MakeSign($data)
    {
        # 初始化返回结果
        $_receipt = null;
        if (is_array($data) and !empty($data)) {
            # 对数组进行字典序排序
            ksort($data);
            #将参数格式化成url参数
            $buff = null;
            foreach ($data as $k => $v) {
                if ($k != "sign" and $v != "" and !is_array($v)) {
                    $buff .= $k . "=" . $v . "&";
                }
            }
            $buff = trim($buff, "&");
            # 在参数最后拼接上key
            $buff = $buff . "&key=youpi160188924116018892411601889"; #  //注：key为商户平台设置的密钥key 位置：账户中心->api 自己随便设置 记住
            # 将参数进行MD5加密,全部转换成大写
            $_receipt = strtoupper(md5($buff));
        }
        return $_receipt;
    }

    /**
     * @access public
     * @param $data array 传过来的所有数据
     * @return string
     * context 转换成xml格式
     **/
    public function ToXml($data)
    {
        if (!is_array($data) || count($data) <= 0) {
            return false;
        }
        $xml = "<xml>";
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * @access public
     * @param $xml array 传过来的所有数据
     * @return string
     * context 将xml转换成数组
     **/
    public function FromXml($xml)
    {
        if (!$xml) {
            return false;
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $_receipt = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $_receipt;
    }




    /**
     * @access public
     * @param string $url 访问地址
     * @param string/array 访问参数，（k/v）数组结构
     * @param string/int $type 请求值类型 0：from 表单请求，1：json json字符串请求，2：xml xml文本标记请求
     * @param bool $useCert 是否使用证书
     * @param int $second 设置超时时间
     * @return mixed
     * @content get请求函数
     */
    function postXmlCurl($url, $param, $type = 'from', $useCert = false, $second = 30)
    {
        $_receipt = null;
        if (!is_null($url)) {
            $_curl = curl_init();
            curl_setopt($_curl, CURLOPT_URL, $url);
            curl_setopt($_curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($_curl, CURLOPT_HEADER, false);
            curl_setopt($_curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
            curl_setopt($_curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($_curl, CURLOPT_SSL_VERIFYHOST, false);
            if ($useCert == true) {
                //设置证书
                //使用证书：cert 与 key 分别属于两个.pem文件
                //证书文件请放入服务器的非web目录下
                $sslCertPath = 'C:/Program Files/Cert/apiclient_cert.pem';
                $sslKeyPath = 'C:/Program Files/Cert/apiclient_key.pem';
                $this->GetSSLCertPath($sslCertPath, $sslKeyPath);
                curl_setopt($_curl, CURLOPT_SSLCERTTYPE, 'PEM');
                curl_setopt($_curl, CURLOPT_SSLCERT, $sslCertPath);
                curl_setopt($_curl, CURLOPT_SSLKEYTYPE, 'PEM');
                curl_setopt($_curl, CURLOPT_SSLKEY, $sslKeyPath);
            }
            curl_setopt($_curl, CURLOPT_POST, true);
            if ($type !== 'from' or (is_numeric($type) and $type !== 0)) {
                if ($type === 'json' or (is_numeric($type) and $type === 1)) {
                    if (is_array($param)) {
                        $param = json_encode($param);
                    }
                    curl_setopt($_curl, CURLOPT_HTTPHEADER,
                        array(
                            'Content-Type:application/json;charset=utf-8',
                            'Content-Length:' . strlen($param)
                        )
                    );
                } elseif ($type === 'xml' or (is_numeric($type) and $type === 2)) {
                    curl_setopt($_curl, CURLOPT_HTTPHEADER,
                        array(
                            'Content-Type:text/xml;charset=utf-8'
                        )
                    );
                }
            }
            curl_setopt($_curl, CURLOPT_TIMEOUT, $second);
            curl_setopt($_curl, CURLOPT_POSTFIELDS, $param);
            $_receipt = curl_exec($_curl);
            if ($this->_curl_utf_8)
                # 将会输内容强制转化为utf-8
                $_receipt = mb_convert_encoding($_receipt, 'UTF-8', 'UTF-8,GBK,GB2312,BIG5');
            $this->_curl_receipt['errno'] = curl_errno($_curl);
            $this->_curl_receipt['error'] = curl_error($_curl);

            curl_close($_curl);
        }
        return $_receipt;
    }

    function get_curl_receipt()
    {
        return $this->_curl_receipt;
    }

    /**
     * @access public
     * @return null
     * @context 微信支付回调接口 须在商户平台->产品中心->开发配置 (只要有扫码支付时需要配置)
     */
    function payCallBack()
    {
        Db::name('comback')->insert(['content'=>1]);
        #在回调里 判断支付订单状态  支付成功 调 paysucess() 同时修改对应订单的状态为待发货
        $testxml = file_get_contents("php://input");
        $jsonxml = json_encode(simplexml_load_string($testxml, 'SimpleXMLElement', LIBXML_NOCDATA));
        Db::name('comback')->insert(['content'=>$jsonxml]);
        $result = json_decode($jsonxml, true);//转成数组，

        if (!empty($result)) {
            Db::name('comback')->insert(['content'=>2]);
            //如果成功返回了   $out_trade_no 返回的是订单号
            $out_trade_no = $result['out_trade_no'];
            $total_fee = $result['total_fee'];
            $orderstatus=Db::table("orderspay")->where(array("order_number"=>$out_trade_no))->select();
            Db::name('comback')->insert(['content'=>$total_fee]);
            Db::name('comback')->insert(['content'=>$out_trade_no]);
            #查询订单的状态是否是待付款的状态  为空不执行
            $order = Db::table('member_order')->field('id')->where(array('order_number'=>$out_trade_no,'pay_status'=>3,'order_status'=>1))->find();
            Db::name('comback')->insert(['content'=>$order['id']]);
            if(!empty($order)){
                #证明有这个订单
                if(!empty($orderstatus)){
                    Db::name('comback')->insert(['content'=>456]);
                    if($orderstatus[0]['status']==1){
                        # 支付已成功
                        $_receipt = array('return_code' => 'SUCCESS');
                        $return = $this->ToXml($_receipt);
                        echo($return);
                    }else{
                        $res=$result;
                        unset($res['sign']);
                        $retrun_sign=$this->MakeSign($res);

                        if($result['sign']==$retrun_sign){

                            # 签名相同
                            if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
                                Db::name('comback')->insert(['content'=>123]);
                                # 返回接受成功
                                Db::table("orderspay")->where(array("order_number"=>$out_trade_no))->update(array("status"=>1,"over_time"=>$result['time_end']));
                                $_receipt = array('return_code' => 'SUCCESS');

                                $this->paySuccess($out_trade_no,$order['id'],$total_fee,1);

                                $return = $this->ToXml($_receipt);
                                echo($return);
                            }
                        }
                    }
                }
            }else{
                $_receipt = array('return_code' => 'ERROR');
                $return = $this->ToXml($_receipt);
                echo($return);
            }
        }
    }


    #微信支付轮训状态
    function checkStatus(){
        $order_number=input("post.order_number");
        $res=Db::table("orderspay")->field("status")->where(array("order_number"=>$order_number))->find();
        if($res['status']==1){
            # 订单支付成功
            return array("status"=>1);
        }elseif($res['status']==2){
            # 用户取消支付
            return array("status"=>2);
        }else{
            # 支付失败
            return array("status"=>0);
        }
    }


    #取消微信支付  微信支付时点x
    function cencel(){
        $order_number=input("post.order_number");
        Db::table("orderspay")->data(array("status"=>2))->where(array("order_number"=>$order_number))->update();
        echo(true);
    }



    function demo(){
       /* $order = Db::table('member_order')->field('id')
            ->where(array('order_number'=>'0903100541002327','pay_status'=>3,'order_status'=>1))->find();
        var_dump($order['id']);exit;*/


       /*
        * 15804022055
        * 15942740035
        *13591489719
        *
        * */
//       dump(1111111111);die;
        $date = date("Y-m-d H:i:s");
        $Curl= new Curl();
        $param = array(array('order_id'=>'53','money'=>65,'total_money'=>65,'time'=>"2020-09-20 23:27:51"));
        $params['phone'] = '13998282347';
        $params['logConsumption'] = $param;
        $a = $Curl->post("https://meng.childpi.com/alliance/Api/addIntegral",$params,'json');

        var_dump($a);

    }

    /*
    * @Param $orderNumber 订单号
    * @Param $orderId 订单id
    * @Param $price 订单价格或积分
    * @context 微信支付成功
    * */
    function paySuccess($orderNumber,$orderId,$price,$paytype){
        #$paytype : 1 微信 2 支付宝
        #点击结算 -> 开始下订单 ->
        #接收参数

        $order_number = $orderNumber;

        # $pay_price 支付金额 #加资金池里的钱 微信加微信  支付宝加支付宝  $paytype 1 微信 2支付宝
        if($paytype == 1){
            #微信
            $pay_price = $price/100;
            #添加资金池
            #微信
            Db::table("poll")->where('id = 1')->setInc('wx_price',$pay_price);
        }else{
            #支付宝
            $pay_price = $price;
            #支付宝
            Db::table("poll")->where('id = 1')->setInc('alipay_price',$pay_price);
        }
        $date = date("Y-m-d H:i:s");
        #定义数组
        #订单信息
        $order = Db::table('member_order')
            ->field('id,order_number,address,member_id,pay_type,total_price,receive_phone')
            ->where(array('order_number'=>$order_number))->find();
        $member_id = $order['member_id'];

        #修改对应的订单的订单状态  修改对应订单的支付价格
        Db::table('member_order')->where(array('order_number'=>$order_number))->update(array('pay_status'=>1,'order_status'=>2,'pay_price'=>$pay_price,'pay_time'=>$date,'pay_type'=>$paytype));

        Db::table('member_orderdetail')->where("order_id = $orderId")->update(array('order_status'=>2));

        #加消费记录
        Db::table("log_pay")->insert(array('member_id'=>$member_id,'order_id'=>$orderId,'total_price'=>$pay_price,'create_time'=>$date));
        Db::name('member')->where(array('id'=>$member_id))->setInc('sale_price',$pay_price);
        #调云盟时代的后台接口
        $Curl= new Curl();
        #查询用户手机号
        #此时调云盟时代的接口
        $param = array(array('order_id'=>$orderId,'money'=>$pay_price,'total_money'=>$order['total_price'],'time'=>$date));
        $params['phone'] = $order['receive_phone'];
        $params['logConsumption'] = $param;
        //正式
        $re = $Curl->post("https://meng.childpi.com/alliance/Api/addIntegral",$params,'json');
        if(json_decode($re,true)['status'] == true)
        {
            Db::name('member_order')->where(['id'=>$orderId])->update(['add_integral'=>2]);
        }
        //测试
//        $Curl->post("https://www.8a1.top/alliance/Api/addIntegral",$params,'json');
        #加收支记录
        Db::table("log_income")->insert(array('pay'=>1,'desc'=>'用户订单','total_price'=>$pay_price,'type'=>2,'create_time'=>$date));
        # 加销量 加销售额
        $bizpro_arr=Db::name('member_orderdetail')->where('order_id='.$order['id'])->field('bizpro_id,buy_num,price')->select();
        foreach ($bizpro_arr as $v)
        {
         Db::name('bizpro')->where('id='.$v['bizpro_id'])->setInc('real_num',$v['buy_num']);
         Db::name('bizpro')->where('id='.$v['bizpro_id'])->setInc('total_sale',$v['buy_num']*$v['price']);
        }

        #查询客户openid 是否存在
        /* if(!empty($memberInfo['openid'])){
             # 购买成功,发送订阅消息
             $template = new SendTemplate();
             # 查询第一条订单的详情中的商品名称
 //            $proInfo = implode('、', $templateProName);
             $info = 0 . '元';

             #商品订单
             $info = $templatePrice . '元';
             #推送消息模板
             $template->UserOrderPaySuccess($memberInfo['openid'], $order['order_number'],$info,$integral, $date, '您已经下单成功！我们尽快为你安排服务' );
             // Log::write(json_encode($_log),'debug');
         }*/
    }

    /*
     * param:url
     * @context 分享 获取签名
     * */
    function wxConfig(){
        #查询appid appsecrt
        $payapi = Db::table("payapi")->where("type = 1")->field("appid,appsecrt")->find();
        $config= [
            'app_id' => $payapi['appid'],
            'secret' => $payapi['appsecrt'],
            'token'  => '456789',
            'aes_key' => '',
            // 指定 API 调用返回结果的类型：array(default)/collection/object/raw/自定义类名
            'response_type' => 'array',
            'log' => [
                'default' => 'prod', // 默认使用的 channel，生产环境可以改为下面的 prod
                'channels' => [
                    // 测试环境
                    'dev' => [
                        'driver' => 'single',
                        'path' => ROOT_PATH.'/log/.log',
                        'level' => 'debug',
                    ],
                    // 生产环境
                    'prod' => [
                        'driver' => 'daily',
                        'path' => ROOT_PATH.'/log/.log',
                        'level' => 'info',
                    ],
                ],
            ],
            'expire'=>0,
        ];
        $app = Factory::officialAccount($config);
        #获取地址
        $url=input('post.url');
        $app->jssdk->setUrl($url);
        $APIs = ['updateAppMessageShareData', 'updateTimelineShareData','openLocation',
            'getLocation'];
        //$re = $app->jssdk->buildConfig(array('updateAppMessageShareData', 'updateTimelineShareData'), true);
        $re =$app->jssdk->buildConfig($APIs, false,  false,  false);
        return json(array("data"=>$re));

    }



    /**
     *  发起支付(h5)
     * @author   juzi
     * @blog    https://i.csdn.net/#/uc/profile
     * @version 1.0.0
     * @date    2020年5月1日15:38:16
     * @desc    description
     */
    public function pay()
    {

        $params = input();

        $where_order = [
            ['member_id', '=', cache(\think\facade\Request::header('token'))['id']],
            ['mo.order_number', '=', $params['order_number']],
        ];

        $order_info = Db::name('member_order')->alias('mo')->leftJoin(['member'=>'m'],'m.id=mo.member_id')
            ->where($where_order)->field('mo.receive_phone,mo.order_number,mo.order_status,mo.province,mo.city,mo.area,mo.address,m.openid')->find();
        if (empty($order_info) || 1 < $order_info['order_status'])
        {
            return json(DataReturn('订单不存在或已支付', -1));
        }

        # 统一下单成功
        $orderstatus = Db::table("orderspay")->where(array("order_number" => $order_info['order_number']))->select();
        if (empty($orderstatus)) {
            Db::table("orderspay")->data(array("order_number" => $order_info['order_number'], "sign" => ''))->insert();
        } else {
            Db::table("orderspay")->where(array("order_number" => $order_info['order_number']))->update(array("status" => 0));
        }

        $config= $this->WxPayJssdk($order_info,$params);


        return json(DataReturn('sucess', 0, $config));

    }

    /**
     * 生成jssdk参数.
     * @author   juzi
     * @blog    https://i.csdn.net/#/uc/profile
     * @version 1.0.0
     * @date    2020年5月1日15:38:16
     * @desc    description
     */
    private function WxPayJssdk($order_info,$params){

        $wepayapp = new WxService(['wepay'=>true]);

        if ($order_info['receive_phone']=='13080745917') {
            $params['price']=0.01;
        }
        $wepayapp=$wepayapp->GetPayApp();
        $data=[
            'body' => $order_info['province'].$order_info['city'].$order_info['area'].$order_info['address'],
            'out_trade_no' => $params['order_number'],
            'total_fee' => $params['price'] * 100,
            'trade_type' => 'JSAPI',
            'notify_url' => __MY_PUBLIC_URL__ . '/api/WxpayService/payCallBack',
            'openid' => $order_info['openid'],
        ];


        $result = $wepayapp->order->unify($data);

        if($result['return_code']!='SUCCESS')
        {

            throw new BaseException(['code'=>403 ,'errorCode'=>4000,'msg'=>$result['return_msg'],'status'=>false,'debug'=>false ]);

        }
        $jssdk = $wepayapp->jssdk;
         //$config = $jssdk->sdkConfig($result['prepay_id']);
        $config = $jssdk->bridgeConfig($result['prepay_id'],false);
        return $config;
    }
}